package com.yu.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.yu.app.common.enums.OrderStatus;
import com.yu.app.common.enums.OrderTakeType;
import com.yu.app.common.util.session.SessionUtil;
import com.yu.app.mapper.GoodsMapper;
import com.yu.app.mapper.OrderGoodsMapper;
import com.yu.app.mapper.OrderMapper;
import com.yu.app.pojo.OrderGoods;
import com.yu.app.pojo.OrderInfo;
import com.yu.app.pojo.dto.OrderInfoDTO;
import com.yu.app.pojo.dto.UserDTO;
import com.yu.app.pojo.form.CreateOrderParams;
import com.yu.app.pojo.vo.GoodsVO;
import com.yu.app.pojo.vo.HistoryOrderVO;
import com.yu.app.service.OrderService;
import com.yu.common.config.property.AppProperty;
import com.yu.common.config.property.WeixinProperty;
import com.yu.common.exception.ServiceException;
import com.yu.common.service.LockService;
import com.yu.common.service.RedisService;
import com.yu.common.util.GeneratorUtil;
import com.yu.common.util.ip.IpUtil;
import com.yu.common.util.servlet.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    private RedisService redisService;
    @Resource
    private LockService lockService;
    @Resource
    private OrderMapper orderMapper;
    @Resource
    private OrderGoodsMapper orderGoodsMapper;
    @Resource
    private GoodsMapper goodsMapper;
    @Resource
    private WeixinProperty weixinProperty;
    @Resource
    private WxPayService wxPayService;
    @Resource
    private AppProperty appProperty;
    @Resource
    private GoodsServiceImpl goodsService;

    // 创建订单
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String createOrder(CreateOrderParams orderParams) throws ServiceException {
        String wxOpenid = SessionUtil.getCurrentUserId();
        if (!wxOpenid.equals(orderParams.getWxOpenid())) {
            throw ServiceException.CONST_create_order_fail; // 用户的openid不对，
        }
        if (orderParams.getTotalPrice() < 0 || orderParams.getPayPrice() < 0) {
            throw ServiceException.CONST_order_price_error; // 订单价格异常，
        }
        if (CollectionUtils.isEmpty(orderParams.getGoodsVOList())) {
            throw ServiceException.CONST_order_not_has_goods; // 订单里没有商品
        }
        // 外卖配送 检验参数
        if (OrderTakeType.ENUM_take_out.text.equals(orderParams.getTakeType()))
            if (StringUtils.isEmpty(orderParams.getAddressDetail())
                    || orderParams.getAddressDetail().trim().length() < 3 // 尽量确保收货地址
                    || StringUtils.isEmpty(orderParams.getUserPhone())
                    || StringUtils.isEmpty(orderParams.getReceiver()))
                throw ServiceException.CONST_receiver_info_params_invalid;

        // redis实现的乐观锁，防止重复下单和频繁下单
        if (lockService.tryLock(wxOpenid, System.currentTimeMillis() + "", 20)) {
            try {
                OrderInfo orderInfo = new OrderInfo();
                BeanUtils.copyProperties(orderParams, orderInfo);
                orderInfo.setOrderNo(GeneratorUtil.generateOrderNo("1"));
                orderInfo.setOrderStatus(OrderStatus.ENUM_has_not_pay_money.text);
                orderInfo.setCreateTime(new Date());

                // 重新计算订单价格和生成订单相关信息
                int totalPrice = 0;
                int payPrice = 0;
                StringBuilder goodsPreview = new StringBuilder(); // eg: 霸气橙子*1, 烧仙草*2
                int goodsTotalNum = 0; // 商品的总数量
                for (GoodsVO goodsVO : orderParams.getGoodsVOList()) {
                    OrderGoods orderGoods = new OrderGoods();
                    orderGoods.setOrderNo(orderInfo.getOrderNo());
                    orderGoods.setGoodsId(goodsVO.getId());
                    orderGoods.setGoodsName(goodsVO.getName());
                    orderGoods.setGoodsProperty(goodsVO.getPropertyStr());
                    orderGoods.setBuyNum(goodsVO.getNumber());
                    orderGoods.setRealPrice(goodsVO.getRealPrice());
                    // 设置订单的商品信息
                    if (StringUtils.isEmpty(goodsVO.getPropertyStr())) {
                        goodsPreview.append(goodsVO.getName()).append("*").append(goodsVO.getNumber()).append(",");
                    } else {
                        goodsPreview.append(goodsVO.getName()).append("[").append(goodsVO.getPropertyStr()).append(("]"))
                                .append("*").append(goodsVO.getNumber()).append(",");
                    }
                    goodsTotalNum += goodsVO.getNumber();
                    totalPrice += goodsVO.getRealPrice() * goodsVO.getNumber(); // TODO 这里可以去数据库里找对应的realPrice
                    payPrice += goodsVO.getRealPrice() * goodsVO.getNumber();
                    orderGoodsMapper.insert(orderGoods);
                }
                goodsPreview.deleteCharAt(goodsPreview.length() - 1);
                orderInfo.setGoodsPreview(goodsPreview.toString());
                orderInfo.setGoodsTotalNum(goodsTotalNum);

                if (OrderTakeType.ENUM_take_out.text.equals(orderParams.getTakeType())) {
                    totalPrice += appProperty.getPackingPrice();
                    totalPrice += appProperty.getSendingPrice();
                    payPrice += appProperty.getPackingPrice();
                    payPrice += appProperty.getSendingPrice();
                }

                if (totalPrice != orderParams.getTotalPrice() || payPrice != orderParams.getTotalPrice())
                    throw ServiceException.CONST_order_price_error;

                orderMapper.insert(orderInfo);
                return orderInfo.getOrderNo();
            } catch (Exception e) { // 下单失败就释放锁 成功的话就让锁自然过期 避免重复下单或频繁下单
                lockService.releaseLock(wxOpenid);
                throw e;
            }
        } else {
            throw ServiceException.CONST_repeat_create_order;
        }
    }

    // 微信小程序预先支付
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Object wxPrepay(String orderNo, String ip) throws ServiceException {
        UserDTO userDTO = SessionUtil.getCurrentUserDTO();
        OrderInfo orderInfo = orderMapper.selectById(orderNo);

        String tradeType = WxPayConstants.TradeType.JSAPI;
        try {
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            // 设置微信请求基本信息
            orderRequest.setAppid(weixinProperty.getAppid());
            // 区分回调 直接通过 S 来判断
            orderRequest.setOutTradeNo(orderNo); // 商户系统内部的订单号
            orderRequest.setBody("购买_" + orderNo); // 商品描述
            orderRequest.setOpenid(userDTO.getWxOpenid());
            orderRequest.setTotalFee(orderInfo.getPayPrice()); // 订单总金额，单位为分
            orderRequest.setSpbillCreateIp(IpUtil.getIp(ServletUtils.getRequest())); // APP和网页支付提交用户端ip
            orderRequest.setTradeType(tradeType); // JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
            return wxPayService.createOrder(orderRequest);
        } catch (WxPayException e) {
            log.error("[微信支付] 异常", e);
            throw ServiceException.CONST_weixin_pay_exception;
        } catch (Exception e) {
            log.error("[预付款异常]", e);
            throw ServiceException.CONST_weixin_pay_exception;
        }
    }


    @Override
    public Page<HistoryOrderVO> getHistoryOrderByPage(Integer pageNo, Integer pageSize) throws ServiceException {
        String wxOpenid = SessionUtil.getCurrentUserWxOpenid();
        Page<HistoryOrderVO> page = new Page<>(pageNo, pageSize);
        page.setRecords(orderMapper.getHistoryOrderByPage(wxOpenid, (pageNo - 1) * pageSize, pageSize));
        page.setTotal(orderMapper.getHistoryOrderTotalCount(wxOpenid));
        return page;
    }

    @Override
    public OrderInfoDTO getOrderDetail(String orderNo) throws ServiceException {
        OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
        OrderInfo orderInfo = orderMapper.selectById(orderNo);
//        if (!orderInfo.getWxOpenid().equals(SessionUtil.getCurrentUserId())) {
//            throw ServiceException.CONST_order_not_belong_you;
//        }
        BeanUtils.copyProperties(orderInfo, orderInfoDTO);
        orderInfoDTO.setOrderGoodsList(goodsService.getOrderGoodsList(orderNo));
        return orderInfoDTO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean refund(String orderNo, String reason) throws ServiceException {
//        OrderInfo orderInfo = orderMapper.selectById(orderNo);
//        if(!orderInfo.getUserId().equals(SessionUtil.getCurrentUserId())
//                || !orderInfo.getOrderStatus().equals(OrderStatus.ENUM_has_canceled.text)){
//            throw ServiceException.CONST_can_not_refund;
//        }
//
//        if (OrderStatusType.refundable(orderDO.getStatus())) {
//            OrderDO updateOrderDO = new OrderDO();
//            updateOrderDO.setRefundReason(reason);
//            updateOrderDO.setStatus(OrderStatusType.REFUNDING.getCode());
//            changeOrderSubStatus(orderNo, orderDO.getStatus(), updateOrderDO);
//            GlobalExecutor.execute(() -> {
//                OrderDTO orderDTO = new OrderDTO();
//                BeanUtils.copyProperties(orderDO, orderDTO);
//                List<OrderSkuDO> orderSkuList = orderSkuMapper.selectList(new QueryWrapper<OrderSkuDO>().eq("order_no", orderDO.getOrderNo()));
//                orderDTO.setSkuList(orderSkuList);
//                adminNotifyService.refundOrder(orderDTO); // 通知到后台管理员 让他们审核处理
//            });
//            return "ok";
//        }


        return true;
    }

    // 订单退款
//    public void refundOrder(OrderDTO orderDTO) {
//        try {
//            FormBody formBody = new FormBody.Builder()
//                    .add("_gp", "template")
//                    .add("_mt", "refundOrderNotify")
//                    //要通知的管理员Id
//                    .add("userId", "admin")
//                    .add("orderNo", orderDTO.getOrderNo())
//                    .add("refundPrice", "￥" + ((orderDTO.getActualPrice() - orderDTO.getFreightPrice()) / 100.0))
//                    .add("skuInfo", orderDTO.getSkuList().stream().map(item -> (item.getSpuTitle() + "-" + item.getTitle() + " * " + item.getNum())).collect(Collectors.joining("\r\n")))
//                    .add("appId", unimallAdminNotifyProperties.getUniNotifyAppId())
//                    .add("timestamp", System.currentTimeMillis() + "")
//                    .build();
//            String sign = getSign(formBody);
//            okHttpClient.newCall(new Request.Builder().url(unimallAdminNotifyProperties.getUniNotifyUrl() + "?sign=" + sign).post(formBody).build()).execute().body().string();
//        } catch (Exception e) {
//            logger.error("[通知管理员] 异常", e);
//        }
//    }
//    private String getSign(FormBody formBody) throws Exception {
//        Set<String> sortSet = new TreeSet<>();
//        for (int i = 0; i < formBody.size(); i++) {
//            sortSet.add(formBody.value(i));
//        }
//        sortSet.add(this.unimallAdminNotifyProperties.getUniNotifyAppSecret());
//        return SHAUtil.sha256Encode(URLEncoder.encode(sortSet.stream().collect(Collectors.joining()), "utf-8"));
//    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public Integer cancelOrder(String orderNo) throws ServiceException {
        OrderInfo orderInfo = orderMapper.selectById(orderNo);
        if (orderInfo == null)
            return 0;
        // 只有在未付款的时候才能取消订单
        if (!orderInfo.getWxOpenid().equals(SessionUtil.getCurrentUserId())
                || !orderInfo.getOrderStatus().equals(OrderStatus.ENUM_has_not_pay_money.text))
            throw ServiceException.CONST_can_not_cancel_order;

        orderGoodsMapper.delete(new QueryWrapper<OrderGoods>().eq("order_no", orderNo));
        return orderMapper.deleteById(orderNo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Integer finishedOrder(String orderNo) throws ServiceException {
        OrderInfo orderInfo = orderMapper.selectById(orderNo);
        // '请取餐' 或 '已送达' 时用户才能让去确认收货
        if (orderInfo.getWxOpenid().equals(SessionUtil.getCurrentUserId())
                || orderInfo.getOrderStatus().equals(OrderStatus.ENUM_please_take_meal.text)
                || orderInfo.getOrderStatus().equals(OrderStatus.ENUM_has_received.text))
            return orderMapper.finishedOrder(orderNo, OrderStatus.ENUM_has_completed.text);

        throw ServiceException.CONST_confirm_receive_failed;
    }

    // 获取正在处理的订单
    @Override
    public List<OrderInfoDTO> getHandlingOrders() throws ServiceException {
        String wxOpenid = SessionUtil.getCurrentUserWxOpenid();
        List<String> handlingOrderStatusList = new ArrayList<String>() {{
            add(OrderStatus.ENUM_has_completed.text);
            add(OrderStatus.ENUM_has_canceled.text);
            add(OrderStatus.ENUM_has_refunded.text);
        }};
        List<OrderInfo> orderInfos = orderMapper.selectList(new QueryWrapper<OrderInfo>()
                .eq("wx_openid", wxOpenid)
                .notIn("order_status", handlingOrderStatusList)
                .orderByDesc("create_time").last(" limit 10"));

        List<OrderInfoDTO> list = new ArrayList<>();
        for (OrderInfo orderInfo : orderInfos) {
            OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
            BeanUtils.copyProperties(orderInfo, orderInfoDTO);
            orderInfoDTO.setOrderGoodsList(goodsService.getOrderGoodsList(orderInfo.getOrderNo()));
            list.add(orderInfoDTO);
        }
        return list;
    }
}
